﻿using System;
using System.Collections;
using System.Collections.Generic;

namespace Collatz
{
    /// <summary>
    /// 表示一个 Collatz 序列
    /// </summary>
    public class CollatzSerial : IEnumerable<long>, IEquatable<CollatzSerial>
    {
        public const string StartMustBeAPositiveInteger = "序列开头的数值必须是正整数";

        /// <summary>
        /// 以指定的数值为始创建一个 Collatz 序列
        /// </summary>
        /// <param name="start">作为序列开头的数值</param>
        /// <exception cref="ArgumentOutOfRangeException">当<paramref name="start"/>不是正整数时引发</exception>
        public CollatzSerial(long start)
        {
            if (start <= 0)
            {
                throw new ArgumentOutOfRangeException(nameof(start), start, StartMustBeAPositiveInteger);
            }
            _start = start;
        }

        /// <summary>
        /// 表示此序列的开头数值
        /// </summary>
        private readonly long _start;

        /// <summary>
        /// 表示此序列的结束数值
        /// </summary>
        private readonly int _end = 1;

        /// <summary>
        /// 获取此序列的开头数值
        /// </summary>
        public long Start => _start;

        public IEnumerator<long> GetEnumerator()
        {
            var current = _start;

            yield return current;

            while (current != _end)
            {
                if (current % 2 == 0)
                {
                    current = current / 2;
                }
                else
                {
                    current = current * 3 + 1;
                }
                yield return current;
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public bool Equals(CollatzSerial other)
        {
            if (other == null)
            {
                return false;
            }

            if (_start == other._start)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public override bool Equals(object obj)
        {
            if (obj == null)
            {
                return false;
            }

            var serial = obj as CollatzSerial;
            if (serial == null)
            {
                return false;
            }
            else
            {
                return Equals(serial);
            }
        }

        public override int GetHashCode()
        {
            return _start.GetHashCode();
        }

        public static bool operator == (CollatzSerial serial1, CollatzSerial serial2)
        {
            if (((object)serial1) == null || ((object)serial2) == null)
            {
                return Equals(serial1, serial2);
            }

            return serial1.Equals(serial2);
        }

        public static bool operator != (CollatzSerial serial1, CollatzSerial serial2)
        {
            if (((object)serial1) == null || ((object)serial2) == null)
            {
                return !Equals(serial1, serial2);
            }

            return !serial1.Equals(serial2);
        }
    }
}
